/**
   \file      Log.cpp
   \brief     Base class for the logging system.
   \author    Marco Siciliano
   
   LibLog implementation.
   Version 0.1.
   A library to log into several devices.
   Copyright (C) Marco Siciliano 2002 [marco@siciliano.de]
   
   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.
   
   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.
   
   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

#include <iostream>
#include <algorithm>
#include <time.h>
#include <cassert>

#include "BaseLog.hpp"
#include "Log.hpp"




CLog * CLog::s_pInstance = 0;

CLog * CLog::instance()
{
    if(s_pInstance == 0)
    {
		s_pInstance = new CLog;
    }

    return s_pInstance;
}



CLog::CLog() : m_bDebug(false), m_layer(0)
{ 

}


CLog::~CLog()
{
    T_lBaseLogs.clear();
}




void CLog::setFilter(unsigned int filter)
{
    FILTER = filter;
}


bool CLog::registerLogDevice(CBaseLog *pLogDevice)
{
    if(m_bDebug) 
    {
		std::cout << "CLog::registerLogDevice: try to register BaseLogDevice="
                  << pLogDevice->name() 
				  << " [" << pLogDevice << "]" << std::endl;
    }

    if(std::find(T_lBaseLogs.begin(), T_lBaseLogs.end(), pLogDevice) != T_lBaseLogs.end())
	{
		std::cerr << "CLog::registerLogDevice: BaseLogDevice=" 
                  << pLogDevice->name()
				  << " [" << pLogDevice << "]" << " is already registerd" 
                  << std::endl;
		return false;
	}
    
    T_lBaseLogs.push_back(pLogDevice);
    T_lBaseLogs.sort();
	

    if(m_bDebug) 
    {
        std::cout << "CLog::registerLogDevice: registered BaseLogDevice=" 
                  << pLogDevice->name() 
				  << " [" << pLogDevice << "]" << std::endl;
    }

    return true;
}


bool CLog::unregisterLogDevice(CBaseLog *pLogDevice)
{
    if(m_bDebug) 
    { 
        std::cout << "CLog::unregisterLogDevice: try to unregister BaseLogDevice=" 
                  << pLogDevice->name() 
				  << " [" << pLogDevice << "]" << std::endl;
    }


	T_itBaseLog = std::find( T_lBaseLogs.begin(), T_lBaseLogs.end(), pLogDevice);
	
	if(T_itBaseLog == T_lBaseLogs.end())
	{
	    std::cerr << "CLog::unregisterLogDevice: BaseLogDevice=" << pLogDevice->name()
				  << " [" << pLogDevice << "]" << " isn't registerd" << std::endl;
	    return false;
	}
	T_lBaseLogs.erase(T_itBaseLog);

    if(m_bDebug) 
    {
        std::cout << "CLog::unregisterLogDevice: unregistered BaseLogDevice=" 
                  << pLogDevice->name() 
				  << " [" << pLogDevice << "]" << std::endl;
    }

    return true;
}


bool CLog::getMicroSeconds(long &microsec)
{
    struct timeval  tv;
    struct timezone tz;
        
    if(gettimeofday(&tv, &tz) == -1)
    {
        microsec = 0;
        return false;
    }
    
    microsec = tv.tv_usec;
    return true;
}

bool CLog::getDateAndTime(std::string &strDateTime)
{
    struct tm *p;
    struct tm tmbuf;
    time_t  Today;

    if(time(&Today) == (time_t)-1)
    {
       	std::cerr << "CLog::getDateAndTime: system call time() failed" 
                  << std::endl;
		return false;
    }

    if((p = localtime_r(&Today, &tmbuf)) == NULL)
    {
        std::cerr << "CLog::getDateAndTime: system call localtime_r() failed" << std::endl;
		return false;  
    }


    char timestamp[16] = {0};
    sprintf(timestamp, "%02d.%02d. %02d:%02d:%02d",  
			p->tm_mday, p->tm_mon + 1, p->tm_hour, p->tm_min, p->tm_sec);
 
    strDateTime = std::string(timestamp);

    return true;
}


inline const std::string CLog::buildMessage(const std::string &eventSource, 
											const std::string &eventLine, 
											const std::string &eventMessage, 
											CLog::LOG_MESSAGE_TYPE logMessageType)
{
    std::string strTimeDate; 
    if(!getDateAndTime(strTimeDate))
    {
        return "-->error in CLog::buildMessage: getDateAndTime<--\n";
    }


    long microsec;
    if(!getMicroSeconds(microsec))
    {
        return "-->error in CLog::buildMessage: getMicroSeconds<--\n";
    }
    std::stringstream strStream;
    strStream << microsec;


    std::string ret( m_layer, ' ' ); // make indent first
    
    if (m_bDebug) {
        std::cout << "m_layer = " << m_layer << std::endl;
        std::cout << "ret = " << ret << std::endl;
    }
    

    ret += "["; 
    ret += eventMessage;
    ret += "]<";
    ret += eventSource;
    ret += ":";
    ret += eventLine;
    ret += "><";
    switch(logMessageType)
    {
		case CLog::DEBUG:
			ret += "DEBUG";
			break;
		case CLog::MESSAGE:
			ret += "MESSAGE";
			break;
		case CLog::ERROR:
			ret += "ERROR";
			break;
		case CLog::FATAL:
			ret += "FATAL";
			break;
		default:
			{   
				std::cerr << "CLog::buildMessage: Invalid logDeviceType" << std::endl;
				return "";
			}
    }
    ret += "><";
    ret += strTimeDate;
    ret += "><Clk=";
    ret += strStream.str();
    ret += ">\n";
    
    return ret;
}






void CLog::logDebug(const std::string &eventSource, 
					const std::string &eventLine, 
					const std::string &eventMessage)
{
    for(T_itBaseLog = T_lBaseLogs.begin(); T_itBaseLog != T_lBaseLogs.end(); ++T_itBaseLog)
    {
		(*T_itBaseLog)->logDebug(buildMessage(eventSource, eventLine, eventMessage, CLog::DEBUG));
    }
}


void CLog::logMessage(const std::string &eventSource, 
					  const std::string &eventLine, 
					  const std::string &eventMessage)
{
    for(T_itBaseLog = T_lBaseLogs.begin(); T_itBaseLog != T_lBaseLogs.end(); ++T_itBaseLog)
    {
		(*T_itBaseLog)->logMessage(buildMessage(eventSource, eventLine, eventMessage, CLog::MESSAGE));
    }
}

void CLog::logError  (const std::string &eventSource, 
					  const std::string &eventLine, 
					  const std::string &eventMessage)
{
	for(T_itBaseLog = T_lBaseLogs.begin(); T_itBaseLog != T_lBaseLogs.end(); ++T_itBaseLog)
	{
		(*T_itBaseLog)->logError(buildMessage(eventSource, eventLine, eventMessage, CLog::ERROR));
	}
}

void CLog::logFatal  (const std::string &eventSource, 
					  const std::string &eventLine,
					  const std::string &eventMessage)
{
    for(T_itBaseLog = T_lBaseLogs.begin(); T_itBaseLog != T_lBaseLogs.end(); ++T_itBaseLog)
    {
		(*T_itBaseLog)->logFatal(buildMessage(eventSource, eventLine, eventMessage, CLog::FATAL));
    }
}

void CLog::indent()
{
    assert(m_layer >= 0);
    m_layer += 1;
}

void CLog::unindent()
{
    m_layer -= 1;
    assert(m_layer >= 0);
}
